#include "FilePath.h"

using namespace std;

const char* FilePath::seperators = "\\/";

FilePath::FilePath(ResolveType resolveType, FilePath::Type pathtype): type(pathtype), resolve(resolveType)
{
	SetSeperator(type);
}

FilePath::~FilePath()
{
}

FilePath::FilePath(const std::string& path, ResolveType resolveType, FilePath::Type pathtype): type(pathtype), resolve(resolveType)
{
	SetSeperator(type);
	SetPath(path);
}

FilePath::FilePath(const FilePath& rhs): type(rhs.type), resolve(rhs.resolve), seperator(rhs.seperator), volume(rhs.volume), dirs(rhs.dirs)
{
}

void FilePath::SetSeperator(FilePath::Type pathtype)
{
	if (pathtype == WINDOWS)
		seperator = '\\';
	else 
		seperator = '/';
}

FilePath& FilePath::operator=(const FilePath& rhs)
{
	type = rhs.type;
	seperator = rhs.seperator;
	volume = rhs.volume;
	dirs = rhs.dirs;
	resolve = rhs.resolve;

	return *this;
}

bool FilePath::IsVolume(const std::string& dir)
{
	switch(type)
	{
		case WINDOWS:
			return IsWindowsVolume(dir);
		case UNIX:
			return IsUnixVolume(dir);
	}

	return false;
}

bool FilePath::IsUnixVolume(const std::string& dir)
{
	if (dir.length() == 0)
		return true;
	else
		return false;
}

bool FilePath::IsWindowsVolume(const std::string& dir)
{
	bool result = false;
	string::size_type length = dir.length();

	if (length == 2)
	{
		char seperatorchar = dir[1];
		char diskchar = dir[0];

		// if the last character ends with a volume seperator
		if (tolower(diskchar) >= 'a' && tolower(diskchar) <= 'z' && seperatorchar == ':')
			result = true;
	}

	return result;
}

void FilePath::Clear()
{
	volume.clear();
	dirs.clear();
}

void FilePath::SetPath(const std::string& path)
{
	Clear();

	if (path.length())
	{
		BuildList(path);
		FilterVolume(true);

		if (resolve == AUTORESOLVE)
			FilterList();
	}
}

FilePath FilePath::Parent() const
{
	FilePath result = *this;

	if (result.dirs.size())
	{
		result.dirs.pop_back();
	}

	return result;
}

void FilePath::Append(const FilePath& rhs, bool ignoreVolume)
{
	if (rhs.dirs.size())
	{
		if (!ignoreVolume && rhs.IsAbsolute())
		{
			throw "FilePath: Cannot append an absolute path";
		}

		dirs.insert( dirs.end(), rhs.dirs.begin(), rhs.dirs.end() );

		if (resolve == AUTORESOLVE)
			FilterList();
	}
}

void FilePath::Resolve()
{
	FilterList();
}

void FilePath::BuildList(const std::string& path)
{
	bool first = true;
	string::size_type startpos = 0;

	// Keep adding subdirs to the list
	while(startpos != string::npos)
	{	
		string::size_type endpos = path.find_first_of(seperators, startpos);
		string::size_type sublength;


		if (endpos == -1)
			sublength = path.length() - startpos;
		else
			sublength = endpos - startpos;

		string subdir = path.substr(startpos, sublength);

		// Add sub directory to the list
		dirs.push_back(subdir);

		// Skip to next non-seperator
		startpos = path.find_first_not_of(seperators, endpos);
	}
}

bool FilePath::IsSinglePoint(const std::string& dir) const
{
	if (dir == ".")
		return true;
	else
		return false;
}

bool FilePath::IsDoublePoint(const std::string& dir) const
{
	if (dir == "..")
		return true;
	else
		return false;
}

void FilePath::FilterVolume(bool writeback)
{
	if (dirs.size())
	{
		list<string>::iterator firstit = dirs.begin();

		if (IsVolume(*firstit))
		{
			if (writeback)
			{
				volume = *firstit;
				volume.append(&seperator, 1);
			}

			dirs.erase(firstit);
		}
	}
}

void FilePath::FilterList()
{
	list<string>::iterator it = dirs.begin();

	while(it != dirs.end())
	{
		string& subdir = *it;

		if (IsSinglePoint(subdir))
		{
			it = dirs.erase(it);
		}
		else if (IsDoublePoint(subdir))
		{
			list<string>::iterator pointit = it;

			if (it != dirs.begin())
			{
				list<string>::iterator previousdirit = it;
				list<string>::iterator nextdirit = it;

				--previousdirit;
				++nextdirit;

				it = dirs.erase(previousdirit, nextdirit);
			}
			else
			{
				it = dirs.erase(it);
			}
		}
		else
		{
			++it;
		}
	}
}


FilePath FilePath::operator+(const FilePath& rhs) const
{
	FilePath result = *this;

	result += rhs;

	return result;
}

FilePath& FilePath::operator+=(const FilePath& rhs)
{
	Append(rhs, true);

	return *this;
}

bool FilePath::operator==(const FilePath& rhs) const
{
	FilePath thisresolved = *this;
	FilePath thatresolved = rhs;
	thisresolved.Resolve();
	thatresolved.Resolve();

	if (thisresolved.PathString() == thatresolved.PathString())
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool FilePath::operator!=(const FilePath& rhs) const
{
	return !operator==(rhs);
}

bool FilePath::HasPrefix(const FilePath& rhs) const
{
	FilePath thisresolved = *this;
	thisresolved.Resolve();
		
	std::string prefix = rhs.PathString();
	std::string fullpath = PathString();

	if (fullpath.substr(0, prefix.length()) == prefix)
		return true;
	else
		return false;
}

std::string FilePath::FilenameString() const
{
	std::string filename;

	if (dirs.size())
	{
		filename = dirs.back();
	}

	return filename;
}

std::string FilePath::PathString() const
{
	std::string path = volume;

	if (dirs.size())
	{
		list<string>::const_iterator it;
		list<string>::const_iterator secondlastit = dirs.end();

		--secondlastit;

		for(it = dirs.begin(); it != dirs.end() && it != secondlastit; ++it)
		{
			path.append(*it);
			path.append(&seperator, 1);
		}

		if (it == secondlastit)
		{
			path.append(*it);
		}
	}

	return path;
}

const std::string& FilePath::VolumeString() const
{
	return volume;
}
